package astoliarskyi.lab2;

import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class CircularBuffer {
	
	private final int[] buffer;
	private int dataAmount;
	private int startPointer;
	private int endPointer;
	
	public static void main(String[] args) {
		CircularBuffer buffer = new CircularBuffer(5);
		List<Runnable> tasks = new LinkedList<>();
		tasks.add(new ConsumerTask("FirstCosumer", buffer, 5));
		tasks.add(new ConsumerTask("SecondConsumer", buffer, 3));
		tasks.add(new ProducerTask("FirstProducer", buffer, 10, 100));
		tasks.add(new ProducerTask("SecondProducer", buffer, 12, 25));
		for (Runnable task : tasks)
			new Thread(task).start();
	}
	
	public CircularBuffer(int dimension){
		if (dimension == 0) dimension = 1;
		buffer = new int[dimension];
		dataAmount = 0;
		startPointer = 0;
		endPointer = 0;
	}
	
	public synchronized void put(int objectToPut){
		while (dataAmount == buffer.length){
			try{
				wait();
			} catch (InterruptedException e){ /* It won't be interrupted. */ }
		}
		buffer[endPointer] = objectToPut;
		if (endPointer + 1 == buffer.length)
			endPointer = 0;
		else
			endPointer = endPointer + 1;
		dataAmount = dataAmount + 1;
		this.notifyAll();
	}
	
	public synchronized int get(){
		while (dataAmount == 0){
			try {
				wait();
			} catch (InterruptedException e) { /* It won't be interrupted. */ }
		}
		int objectToReturn = buffer[startPointer];
		if (startPointer + 1 == buffer.length)
			startPointer = 0;
		else
			startPointer = startPointer + 1;
		dataAmount = dataAmount - 1;
		this.notifyAll();
		return objectToReturn;
	}	
}

class ProducerTask implements Runnable{

	private final String producerName;
	private final CircularBuffer buffer;
	private final int power;
	private final int upTo;
	
	public ProducerTask(String producerName, CircularBuffer circularBuffer, int power, int upTo){
		this.producerName = producerName;
		buffer = circularBuffer;
		this.power = power;
		this.upTo = upTo;
	}

	@Override
	public void run() {
		int objectsLeft = power;
		Random random = new Random();
		while (objectsLeft > 0){
			int randomNumber = random.nextInt(upTo);
			System.out.println(producerName + " produced " + randomNumber);
			buffer.put(randomNumber);
			System.out.println(producerName + " put " + randomNumber + " to buffer");
			objectsLeft--;
		}
	}
}

class ConsumerTask implements Runnable{
	
	private final String consumerName;
	private final CircularBuffer buffer;
	private final int power;
	
	public ConsumerTask(String consumerName, CircularBuffer circularBuffer, int power){
		this.consumerName = consumerName;
		this.buffer = circularBuffer;
		this.power = power;
	}
	
	@Override
	public void run() {
		int objectsLeft = power;
		while (objectsLeft > 0){
			int takenObject = buffer.get();
			System.out.println(consumerName + " consumed " + takenObject);
			objectsLeft--;
		}
		
	}
	
}
